package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.config.WebSocketServer;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.AlertNotifyDto;
import com.zzyl.dto.AlertRuleDto;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.AlertRuleMapper;
import com.zzyl.service.AlertRuleService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.vo.AlertRuleVo;
import com.zzyl.vo.DeviceDataVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @version 1.0
 * @Author 小王要变胖
 * @Date 2025/2/16 10:54
 * @注释
 */
@Service
@Slf4j
public class AlertRuleServiceImpl implements AlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private WebSocketServer webSocketServer;


    @Override
    public void create(AlertRuleDto alertRuleDto) {
        AlertRule bean = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.create(bean);
    }

    @Override
    public PageResponse<AlertRuleVo> getPage(Integer pageNum, Integer pageSize, String alertRuleName, String functionName, String productKey) {
        PageHelper.startPage(pageNum, pageSize);
        Page<AlertRuleVo> page = alertRuleMapper.getPage(alertRuleName, functionName, productKey);
        page.getResult().forEach(alertRuleVo -> {
            String rule = CharSequenceUtil.format("{}{}{},持续{}个周期就报警",
                    alertRuleVo.getFunctionName(), alertRuleVo.getOperator(), alertRuleVo.getValue(), alertRuleVo.getDuration());
            alertRuleVo.setRules(rule);
        });

        return PageResponse.of(page, AlertRuleVo.class);
    }

    @Override
    public AlertRuleVo getById(Long id) {
        AlertRuleVo alertRuleVo = alertRuleMapper.getById(id);
        return alertRuleVo;
    }

    @Override
    public void updateById(Long id, AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRule.setId(id);
        alertRuleMapper.updateById(alertRule);

    }

    @Override
    public void deleteById(Long id) {
        Integer status = alertRuleMapper.selectStatus(id);
        if (status > 0) {
            throw new BaseException(BasicEnum.RULE_IS_START);
        }
        alertRuleMapper.deleteById(id);
    }

    @Override
    public void setStatus(Long id, Integer status) {
        AlertRule alertRule = new AlertRule();
        alertRule.setId(id);
        alertRule.setStatus(status);
        alertRuleMapper.updateById(alertRule);
    }

    @Override
    public void alertFilter(DeviceDataVo deviceDataVo) {
        // 获取设备上报数据时间，如果上报发生在1分钟前，为历史数据，不再处理
        LocalDateTime alarmTime = deviceDataVo.getAlarmTime();
        long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);

        // redis的数据超过当前时间60秒，历史数据，无意义，直接结束
        if (between > 60) {
//            return;
        }

        //准备对应的报警规则数据
        List<AlertRule> alertRuleListAll = alertRuleMapper.selectAllRule(null, null, null);
        List<AlertRule> alertRuleList1 = alertRuleMapper.selectAllRule(deviceDataVo.getIotId(), deviceDataVo.getFunctionId(), deviceDataVo.getProductKey());
        alertRuleListAll.addAll(alertRuleList1);

        // 规则为空，直接结束
        if (ObjectUtil.isEmpty(alertRuleListAll)) {
            return;
        }

        // 遍历每一条规则
        alertRuleListAll.forEach(alertRule -> {
            deviceDateAlarmHandler(deviceDataVo, alertRule);
        });

    }


    @Value("${zzyl.alert.deviceMaintainerRole}")
    private String deviceMaintainerRole;
    //超级管理员的角色名称
    @Value("${zzyl.alert.managerRole}")
    private String managerRole;

    private void deviceDateAlarmHandler(DeviceDataVo deviceDataVo, AlertRule alertRule) {
        // 如果当前数据与匹配规则不一致，直接返回

        if(!ObjectUtil.equals(deviceDataVo.getFunctionId(),alertRule.getFunctionId())){
            return;
        }


        // 判断时间数据是否在规则的生效时间范围内
        LocalTime alarmTime = LocalTime.from(deviceDataVo.getAlarmTime());
        String alertEffectivePeriod = alertRule.getAlertEffectivePeriod();
        String[] split = alertEffectivePeriod.split("~");
        LocalTime startTime = LocalTime.parse(split[0]);
        LocalTime endTime = LocalTime.parse(split[1]);

        // 数据不在规则的生效时间内
        if (alarmTime.isBefore(startTime) || alarmTime.isAfter(endTime)) {
            return;
        }

        // 从redis中获取数据，报警规则连续触发次数rediskey
        String dataKey = deviceDataVo.getProductKey() + deviceDataVo.getIotId() + deviceDataVo.getFunctionId();

        // 判断数据是否到达阈值
        Float dataValue = Float.valueOf(deviceDataVo.getDataValue());
        Float ruleValue = alertRule.getValue();
        int compare = NumberUtil.compare(dataValue, ruleValue);
        if (compare >= 0 && ">=".equals(alertRule.getOperator()) || compare < 0 && "<".equals(alertRule.getOperator())) {
            System.out.println(deviceDataVo.getProductName() + deviceDataVo.getFunctionId() +"达到阈值");
        } else {
            // 没有达到阈值，返回 并且删除阈值的标识键
            redisTemplate.delete(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey);
            return;
        }

        // 根据Key获取沉默周期
        String silent = redisTemplate.opsForValue().get(CacheConstants.ALERT_SILENT_PREFIX + dataKey);
        if (ObjectUtil.isNotEmpty(silent)) {
            // 有值说明在沉默周期范围内
            return;
        }


        String s = redisTemplate.opsForValue().get(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey);
        int count = ObjectUtil.isAllEmpty(s) ? 1 : Integer.parseInt(s) + 1;

        // 当前报警次数，不等于持续周期次数
        if (count != alertRule.getDuration()) {
            redisTemplate.opsForValue().set(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey, count + "");
            boolean result = redisTemplate.opsForValue().setIfAbsent(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey, count + "");
            if(result){
                log.error("redis写入成功");
            }else {
                log.error("redis写入失败");
            }
            return;
        }

        // 符合报警要求，删除redis报警次数，添加redis沉默周期
        redisTemplate.delete(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey);
        redisTemplate.opsForValue().set(CacheConstants.ALERT_SILENT_PREFIX + dataKey, "aaa", alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);


        // 插入告警数据
        // 设备数据是老人数据，还是固定数据
        List<Long> userIds = null;
        // 0：老人异常数据，1：设备异常数据
        if (alertRule.getAlertDataType().equals(0)) {
            // 0：随身设备 1：固定设备
            if (deviceDataVo.getLocationType().equals(0)) {
                userIds = alertRuleMapper.queryDeviceElder(deviceDataVo.getIotId());
            } else {     // 老人的床，固定设备
                userIds = alertRuleMapper.queryDeviceElderAndBed(deviceDataVo.getIotId());
            }
        } else { // 维修员
            userIds = alertRuleMapper.queryDeviceRepair(deviceMaintainerRole);
        }

        // 超级管理员都要执行
        List<Long> managers = alertRuleMapper.queryDeviceRepair(managerRole);
        Collection<Long> userList = CollUtil.addAll(userIds, managers);

        // 批量插入数据库
        List<AlertData> list = new ArrayList<>();
        userList.forEach(userId ->{
            AlertData alertData = new AlertData();
            BeanUtil.copyProperties(deviceDataVo,alertData);
            alertData.setUserId(userId);
            String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警",
                    alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration());
            alertData.setAlertReason(alertReason);
            alertData.setAlertRuleId(alertRule.getId());
            alertData.setType(alertRule.getAlertDataType());
            alertData.setStatus(0);
            list.add(alertData);
        });

        alertRuleMapper.batchInsert(list);


        // websocket通知：弹窗消息
        AlertNotifyDto alertNotifyDto = new AlertNotifyDto();
        AlertData alertData = list.get(0);
        BeanUtil.copyProperties(alertData,alertNotifyDto);
        alertNotifyDto.setFunctionName(alertRule.getFunctionName());
        alertNotifyDto.setAlertDataType(alertData.getType());
        alertNotifyDto.setNotifyType(1);
        //给指定消费者发送消息
        webSocketServer.sendMessageToConsumer(alertNotifyDto, userIds);
    }


}
